#pragma once

#include "Thread.h"
#include "Logger.h"
#include "noncopyable.h"

#include <queue>
#include <mutex>
#include <atomic>
#include <memory>
#include <future>
#include <cstdint>
#include <functional>
#include <unordered_map>
#include <condition_variable>

const int TASK_MAX_THRESHOLD = INT32_MAX; // 最大任务数量
const int THREAD_MAX_THRESHOLD = 100;     // 最大线程数量
const int THREAD_MAX_IDLE_TIME = 60;      // 单位：s

// 线程池支持两种模式
enum class PoolMode
{
    MODE_FIXED,  // 固定数量的线程
    MODE_CACHED, // 线程数量可动态增长
};

// 线程池类型
class ThreadPool : noncopyable
{
public:
    ThreadPool();
    ~ThreadPool();

    // 支持用户设置线程池的工作模式
    void setMode(PoolMode mode);

    // 支持用户设置任务队列的上限阈值
    void setTaskQueMaxThresHold(int threshold);

    // 支持用户设置线程池cached模式下线程上限阈值
    void setThreadSizeThresHold(int threshold);

    // 基于可变参模板支持用户提交各种不同类型的任务
    template <typename Func, typename... Args>
    auto submitTask(Func &&func, Args... args) -> std::future<decltype(func(args...))>
    {
        {
            // 打包任务，放入任务队列
            using RType = decltype(func(args...));
            auto task = std::make_shared<std::packaged_task<RType()>>(
                std::bind(std::forward<Func>(func), std::forward<Args>(args)...)); // 打包任务，将参数绑定至任务函数
            std::future<RType> result = task->get_future();

            // 获取锁
            std::unique_lock<std::mutex> lock(taskQueMtx_);

            // 用户提交任务，最长不能阻塞超过1s，否则判断提交任务失败，返回
            if (!notFull_.wait_for(lock, std::chrono::seconds(1), [&]() -> bool
                                   { return taskQue_.size() < (size_t)taskQueMaxThresHold_; }))
            {
                // wait_for返回为false，说明用户提交任务时最长阻塞超过了1s
                LOG_INFO("Task queue is full, submit task fail. \n");
                auto task = std::make_shared<std::packaged_task<RType()>>(
                    []() -> RType
                    { return RType(); });
                (*task)();
                return task->get_future();
            }

            taskQue_.emplace([task]()
                             { (*task)(); }); // 通过匿名对象调用实际任务
            taskSize_++;

            // 新放任务后，即可在notEmpty_上进行通知 -> 方便“消费者”消费
            notEmpty_.notify_all();

            // cached模式任务处理比较紧急（适合小而快的任务），需要根据任务数量和空闲线程的数量，判断是否需要创建新的线程
            if (poolMode_ == PoolMode::MODE_CACHED && taskSize_ > idleThreadSize_ && curThreadSize_ < threadSizeThresHold_)
            {
                LOG_INFO(">>> create new thread. \n");

                // 创建新线程
                auto ptr = std::make_unique<Thread>(std::bind(&ThreadPool::threadFunc, this, std::placeholders::_1));
                // threads_.emplace_back(std::move(ptr));
                int threadId = ptr->getId();
                threads_.emplace(threadId, std::move(ptr));
                // 启动线程
                threads_[threadId]->start();
                // 修改线程数量相关变量
                curThreadSize_++;
                idleThreadSize_++;
            }

            return result;
        }
    }

    // 开启线程池
    void start(int initThreadSize = std::thread::hardware_concurrency());

private:
    // 定义线程函数
    void threadFunc(int threadId);
    // 检查pool的运行状态
    bool checkRunningState() const { return isPoolRunning_; }

    std::unordered_map<int, std::unique_ptr<Thread>> threads_; // 线程列表
    int initThreadSize_;                                       // 初始线程数量 -> size_t为无符号整型
    std::atomic_int curThreadSize_;                            // 记录当前线程池内线程的总数量
    std::atomic_int idleThreadSize_;                           // 记录当前线程池空闲线程的数量
    int threadSizeThresHold_;                                  // 线程数量上限阈值

    // Task任务 => 函数对象
    using Task = std::function<void()>;
    std::queue<Task> taskQue_; // 任务队列
    std::atomic_int taskSize_; // 原子类型变量记录任务的数量
    int taskQueMaxThresHold_;  // 任务队列数量上限阈值

    std::mutex taskQueMtx_;            // 保证任务队列的线程安全
    std::condition_variable notFull_;  // 表示任务队列不满
    std::condition_variable notEmpty_; // 表示任务队列不空
    std::condition_variable exitCond_; // 等到线程资源全部回收

    PoolMode poolMode_;              // 当前线程池的工作模式
    std::atomic_bool isPoolRunning_; // 当前线程池的启动状态
};